package main

import (
	"encoding/json"
	"fmt"
)

/*
方法（method）介绍
Go语言同时有函数和方法。【方法的本质是函数】，一种作用于【特定类型变量】的函数。但是方法和函数又有所不同：
	1. 函数（function）是一段具有独立功能的代码，可以被反复多次调用，从而实现代码复用；
	2. 方法（method）是【一个结构体】的行为功能，只有该类的对象才能调用；
	3. 特定类型变量叫作接受者（receiver）。接受者的概念类似于传统面向对象语言中的this或self关键字，方法有接受者，而函数无接受者；
	4. Go语言中，接受者可以是结构体，也可以是结构体类型外的其他任何类型；
	5. 函数不可以重名，而方法可以重名；
	6. 方法是可以继承的，如果匿名字段实现了一个方法，那么包含这个匿名字段的struct也能调用该匿名字段中的方法

为什么要使用方法：
	1. Go不是一种纯粹面向对象的编程语言，它不支持类。因此其方法旨在实现类似于类的行为；
	2. 相同名称的方法可以在不同的类型上定义，而具有相同名称的函数是不允许的；
*/
func main() {

	/*
		一. 语法格式：
			func (接受者变量名 接受者类型) 方法名(参数列表) (返回值列表) {  // 方法体 }
	*/
	employee := Employee{
		Name:     "steven",
		Currency: "$",
		Salary:   10000,
	}
	fmt.Printf("对象分配内存地址：%p \n", &employee)
	// 调用函数实现
	printEmployee(employee)
	fmt.Println(employee)
	// 调用方法实现打印
	employee.printEmployee()
	fmt.Println(employee)
	// 调用方法实现打印(指针)
	(&employee).printEmployee2()
	fmt.Println(employee)

	/*
		二. 方法是可以继承的，如果匿名字段实现了一个方法，那么包含这个匿名字段的struct也能调用该匿名字段中的方法
	*/
	student := Student{
		Human: Human{
			"steven",
			"13345323464",
			15,
		},
		school: "上海13中",
	}
	// 调用匿名字段的方法
	student.sayHi() // TIPS： 需要把Student作为接收者的 sayHi() 方法注释掉，才看得到效果

	/*
		三. 方法重写
			方法重写是指一个包含了匿名字段的struct也实现了该匿名字段实现的方法。
			TIPS：当结构体存在继承关系时，方法调用按照就近原则
	*/
	student.sayHi() // TIPS： 需要把Student作为接收者的 sayHi() 方法放开，才看得到效果

	/*
		四. 任意类型添加方法
	*/
	var m1 MyInt
	m1.SayHello() //Hello, 我是一个int。
	m1 = 100
	fmt.Printf("%#v  %T\n", m1, m1) //100  main.MyInt

	/**
		五. 链式调用。通过返回修改后的对象本身（指针），可以轻松地将多个函数接收器链在一起，而无需添加不必要的代码行
	 */
	e := &Employee{
		Name:     "qin",
		Currency: "CN",
		Salary:   10,
	}
	marshal, _ := json.Marshal(*e)
	fmt.Println("链式调用前：", string(marshal))
	e.AddSalary(20).ReName("steven")
	marshal, _ = json.Marshal(*e)
	fmt.Println("链式调用后：", string(marshal))
}

type Employee struct {
	Name, Currency string
	Salary         float64
}

type Human struct {
	name, phone string
	age         int8
}

type Student struct {
	Human
	school string
}

/*
方法（非指针）
*/
func (e Employee) printEmployee() {

	fmt.Printf("方法（非指针）调用内存地址：%p \n", &e)

	fmt.Printf("姓名：%s， 薪资：%s%.4f \n", e.Name, e.Currency, e.Salary)

	e.Name = "非指针作为接受者，不能改变接收者原来的数据"
}

/*
方法（指针）
*/
func (e *Employee) printEmployee2() {

	fmt.Printf("方法（指针）调用内存地址：%p \n", e)

	fmt.Printf("姓名：%s， 薪资：%s%.4f \n", e.Name, e.Currency, e.Salary)

	e.Name = "指针作为接受者，可以改变接收者原来的数据"
}

/*
函数
*/
func printEmployee(e Employee) {

	fmt.Printf("函数调用内存地址：%p \n", &e)

	fmt.Printf("姓名：%s， 薪资：%s%.4f \n", e.Name, e.Currency, e.Salary)
	e.Name = "非指针作为接受者，不能改变接收者原来的数据"
}

func (human Human) sayHi() {

	fmt.Println("大家好! 我是：", human.name, "年龄：", human.age, "电话：", human.phone)
}

func (student Student) sayHi() {

	fmt.Println("大家好! 我对sayHi() 方法重写了，我是：", student.name, "年龄：", student.age, "电话：", student.phone)
}

/*
MyInt 将int定义为自定义MyInt类型
*/
type MyInt int

/*
SayHello 为MyInt添加一个SayHello的方法
*/
func (m MyInt) SayHello() {

	fmt.Println("Hello, 我是一个int。")
}

func (e *Employee) AddSalary(salary float64) *Employee {
	e.Salary = e.Salary + salary
	return e
}

func (e *Employee) ReName(name string) *Employee {
	e.Name = name
	return e
}